﻿using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Configuration;
using Gollie.Mail;
using Gollie.Service;
using NLog;

namespace Gollie.Mail.Queue.Service
{
	internal class MainTask
	{
		private static readonly Logger logger = LogManager.GetCurrentClassLogger();

		internal Task mainTask;
		private static ConcurrentDictionary<int, Task> subTasks = new ConcurrentDictionary<int, Task>();
		CancellationTokenSource cts;

		private static bool isStopping = false;

		internal MainTask(CancellationTokenSource cts)
		{
			logger.Info("Starting GollieMailService");

			this.cts = cts;
			mainTask = Task.Factory.StartNew(() => Run(cts.Token), cts.Token); 
		}

		internal void Run(CancellationToken ct)
		{
			logger.Info("GollieMailService is now running");
			try
			{
				int mailInterval = Convert.ToInt32(ConfigurationManager.AppSettings["mailinterval"]);
				logger.Info("Checking for mails every {0} milliseconds", mailInterval);

				while (true)
				{
					ct.ThrowIfCancellationRequested();

					try
					{
						MailSender.SendAll();
					}
					catch (Exception e)
					{
						logger.FatalException("", e);
					}

					//check for mails every mailinterval ms.
					Task.Delay(mailInterval, ct).Wait(ct);
				}
			}
			catch (OperationCanceledException)
			{
				logger.Info("MainTask exited with CancellationRequested, waiting for all tasks ({0}) to finish", subTasks.Count);

				Task[] tasks = subTasks.Values.ToArray<Task>();
				if (Task.WaitAll(tasks, 10000))
				{
					logger.Info("All tasks have finished");
				}
				else
				{
					logger.Info("Not all tasks could finish in time. Still {0} running. Stopping anyway...", subTasks.Count);
				}
			}
		}

		internal void Cancel()
		{
			isStopping = true;

			logger.Info("Cancelling service tasks...");
 
			if (mainTask == null)
			{
				logger.Info("Main service task is not running.");
				return;
			}

			if (mainTask.IsFaulted)
			{
				logger.Info("Main service task is in faulted state");

				DisposeMainTask();

				return;
			}

			if (mainTask.IsCompleted)
			{
				logger.Info("Main service task is in completed state ");

				DisposeMainTask();

				return;
			}

			logger.Info("Cancelling main task...");
			this.cts.Cancel();
			if (this.WaitMainTask())
				logger.Info("GollieMailService stopped gracefully");
			else
				logger.Warn("Unable to cancel main task within time...GollieMailService stopped non-gracefully");
		}

		private bool WaitMainTask()
		{
			for (var i = 0; i < 6; i++)
			{
				// wait for up to 10 seconds

				logger.Info("Waiting for main task to finish...");
				if (mainTask.Wait(10000))
				{
					// it has stopped
					logger.Info("Main task has stopped gracefully");
					DisposeMainTask();
					return true;
				}

				// ask for an additional 15 seconds

				Thread.Sleep(15000);
			}

			return false;
		}

		private void DisposeMainTask()
		{
			mainTask.Wait();

			mainTask.Dispose();

			mainTask = null;
		}

		internal static bool QueueAsync(EMail email)
		{
			if (isStopping)
				return false;

			Task t = new Task(() => email.DoQueue());
			subTasks.TryAdd(t.Id, t);
			//logger.Debug("After Add: {0} items in taskarray", subTasks.Count);
			Task cont = t.ContinueWith((antecedent) =>
			{
				Task t2; subTasks.TryRemove(antecedent.Id, out t2); if (t2 != null) t2.Dispose(); /* logger.Debug("After Remove: {0} items in taskarray", subTasks.Count); */
			});

			t.Start();

			return true;
		}

		internal static bool QueueAsync(EMailCollection emails)
		{
			if (isStopping)
				return false;

			Task t = new Task(() => emails.DoQueue());
			subTasks.TryAdd(t.Id, t);
			//logger.Debug("After Add: {0} items in task array", subTasks.Count);
			Task cont = t.ContinueWith((antecedent) =>
			{
				Task t2; subTasks.TryRemove(antecedent.Id, out t2); t2.Dispose(); //logger.Debug("After Remove: {0} items in taskarray", subTasks.Count);
			});

			t.Start();

			return true;
		}

		internal static bool QueueAsync(BulkMail emails)
		{
			if (isStopping)
				return false;

			Task t = new Task(() => emails.DoQueue());
			subTasks.TryAdd(t.Id, t);
			//logger.Debug("After Add: {0} items in task array", subTasks.Count);
			Task cont = t.ContinueWith((antecedent) =>
			{
				Task t2; subTasks.TryRemove(antecedent.Id, out t2); t2.Dispose(); //logger.Debug("After Remove: {0} items in taskarray", subTasks.Count);
			});

			t.Start();

			return true;
		}
	}
}
